Skip to content

Add Kotlin implementation to find missing number in a list#1333

Open
Balaviknesh wants to merge 1 commit intosuper30admin:masterfrom
Balaviknesh:master
Open

Add Kotlin implementation to find missing number in a list#1333
Balaviknesh wants to merge 1 commit intosuper30admin:masterfrom
Balaviknesh:master

Conversation

@Balaviknesh
Copy link

No description provided.

@super30admin
Copy link
Owner

Strengths:

  • You have chosen an efficient algorithm (binary search) for the problem, which is good for sorted arrays.
  • The code is clean and well-organized with proper variable names.
  • You handled the case when no number is missing by returning -1.

Areas for Improvement:

  • The solution does not account for cases where the missing number is the first element (1) or the last element (n). For example, if the input is [2,3,4,5], your code should return 1, but it currently does not.
  • The condition if(list[0] + list.size - 1 == list[list.size - 1]) return -1 is incorrect for detecting no missing number. This condition checks if the last element is equal to the first element plus the length minus one. However, this only works if the array starts at 1. If the array starts at a number greater than 1, this condition will fail. For example, [2,3,4,5] has length 4, and 2 + 4 - 1 = 5, which equals the last element, so your code would return -1, but actually, 1 is missing.
  • The binary search logic is based on the difference between list[mid] and list[0], which is not the standard approach. The standard approach is to compare the value at mid with the expected value (which would be mid + 1 if the array started at 1). However, since the array might not start at 1, you should use the index to compute the expected value. For instance, at index i, the expected value is i + 1 (if the array should start at 1). But if the array does not start at 1, then the expected value might be different. Actually, the problem states that the array contains integers from 1 to n with one missing, so the array should ideally start at 1 and end at n. However, if the missing number is 1, the array will start at 2. Similarly, if the missing number is n, the array will end at n-1.

To fix this, you should use the fact that in a complete array without duplicates, the value at index i should be i + 1. So, you can check if the value at mid is equal to mid + 1. If it is, then the missing number is to the right; otherwise, it's to the left. This is a common approach.

Here is a corrected version of the binary search:

var low = 0
var high = list.size - 1

while (low <= high) {
    val mid = low + (high - low) / 2
    if (list[mid] == mid + 1) {
        low = mid + 1
    } else {
        high = mid - 1
    }
}
return low + 1

This will work for arrays that start at 1. However, if the array does not start at 1 (meaning the missing number is 1), then the first element will be 2. At index0, the value is 2, which is not equal to 1 (0+1). So, the binary search would correctly identify that the missing number is on the left. But note: if the array does not start at 1, then the entire array is shifted. Actually, the problem assumes the array should contain numbers from 1 to n, so if the first element is not 1, then 1 is missing. So, you can first check if the first element is 1. If not, return 1.

Similarly, if the last element is not n (which is list.size + 1), then the missing number is n. So, you should check these edge cases first.

Revised plan:

  1. If the list is empty, return -1.
  2. If the first element is not 1, return 1.
  3. If the last element is not list.size + 1, return list.size + 1.
  4. Otherwise, perform binary search to find the missing number.

Example:
For [2,3,4,5]: first element is 2 != 1, so return 1.
For [1,2,3,4]: last element is 4, but n should be 5 (since size=4, n=5), so return 5.

Then, inside the binary search, we assume that the missing number is between the first and last. The binary search checks for the point where the value is not equal to index+1.

So, the code should be:

fun searchMissingNumber(list: List<Int>): Int {
    val n = list.size + 1
    if (list.isEmpty()) return -1
    if (list[0] != 1) return 1
    if (list[list.size-1] != n) return n

    var low = 0
    var high = list.size - 1
    while (low <= high) {
        val mid = low + (high - low) / 2
        if (list[mid] == mid + 1) {
            low = mid + 1
        } else {
            high = mid - 1
        }
    }
    return low + 1
}

This should handle all cases.

@super30admin
Copy link
Owner


Let's evaluate the student's solution step by step.

First, the problem: Given a sorted array of n-1 integers in the range 1 to n with no duplicates, find the missing number.

The student's solution is written in Kotlin. The approach uses binary search.

Let's break down the code:

1. The function `searchMissingNumber` takes a list of integers and returns an integer.
2. It first checks if the list is empty, then returns -1.
3. Then it checks if the last element is equal to the first element plus the size of the list minus one. This condition is actually checking if there is no missing number? Because if the array has n-1 elements and the range is from 1 to n, then if the last element is exactly the first element plus (n-2) (since index from 0 to n-2) but wait: the list has size = n-1. So the expected last element should be n, which is list[0] + (n-1) - 1? Actually, if the list is from 1 to n with one missing, the last element might be n only if the missing number is not at the end. But if the missing number is at the end, the last element would be n-1.

Actually, the condition: `if(list[0] + list.size - 1 == list[list.size - 1]) return -1` is trying to say: if the array is contiguous (no missing number) then return -1. But note: the problem states that one integer is missing. So this condition might be to handle the case when there is no missing number? However, the problem says "provide an array of n-1 integers", so it is guaranteed that one is missing. So this check might be unnecessary. But it's good to have for robustness.

But wait: what if the array is [1,2,3,4] (n=5, so array has 4 elements). Then the last element is 4, and list[0] + list.size - 1 = 1 + 4 - 1 = 4. So it returns -1. But in this case, the missing number is 5. So this condition is incorrect because it assumes that if the last element is as expected (i.e., the array is contiguous from start to end without gap at the end) then there is no missing number? But actually, the missing number could be at the end. So this condition is flawed.

Let me test with an example: 
Input: [1,2,3,4] (n=5, missing 5). Then the condition: list[0] + list.size - 1 = 1 + 4 - 1 = 4, which equals the last element (4). So it returns -1, but it should return 5. So this condition is wrong.

Therefore, the student's solution has a bug in this boundary check.

Now, the binary search part:

The student uses:
var low = 0
var high = listSize - 1

while (low <= high) {
    val mid = low + (high - low)/ 2
    if (list[mid] - list[0] == mid) {
        low = mid + 1
    } else {
        high = mid -1
    }
}
return list[0] + low

Let's see how this works.

The idea is: in a contiguous array starting from list[0], the value at index i should be list[0] + i.
So if at index mid, the value is list[0] + mid, then the missing number is to the right. Otherwise, it is to the left.

But note: the array might not start at 1? The problem says "range of 1 to n", so the array should start at 1. But what if the missing number is 1? Then the array would start at 2. However, the problem says "no duplicates", and the range is 1 to n, so if the array has n-1 elements, the smallest number might be 1 or 2.

But the problem does not guarantee that the array starts at 1. Actually, the examples start at 1. But if the missing number is 1, then the array would be [2,3,...,n]. So the student's code uses list[0] as the base. So if the array starts at 2, then list[0] is 2.

Now, in the binary search, we are comparing list[mid] - list[0] with mid. If they are equal, it means that from index0 to mid, the numbers are contiguous. So the missing number must be after mid.

But if the missing number is at the beginning, for example: array [2,3,4] (n=4, missing 1). Then:
list[0]=2.
At mid=1: list[1]=3, so list[1]-list[0]=1, and mid=1 -> equal -> so low becomes mid+1=2.
Then low=2, high=2 -> mid=2: list[2]=4, list[2]-list[0]=2, and mid=2 -> equal -> low becomes 3.
Then break, return list[0] + low = 2+3=5. But expected is 1. So it fails.

So the student's solution does not handle the case when the missing number is the first element.

Similarly, what if the missing number is the last element? For example: [1,2,3] (n=4, missing 4).
Then:
list[0]=1.
low=0, high=2.
mid=1: list[1]=2, 2-1=1, mid=1 -> equal -> low=2.
Then mid=2: list[2]=3, 3-1=2, mid=2 -> equal -> low=3.
Return 1+3=4 -> which is correct.

So it works for the last element missing.

But it fails for the first element missing.

Now, what about a missing element in the middle? Example: [1,2,4,5] (n=5, missing 3).
list[0]=1.
low=0, high=3.
mid=1: list[1]=2, 2-1=1, mid=1 -> equal -> low=2.
Then low=2, high=3 -> mid=2: list[2]=4, 4-1=3, but mid=2 -> not equal -> so high=1.
Now low=2, high=1 -> break. Return list[0]+low=1+2=3 -> correct.

So it works for the middle.

But it fails when the missing number is the first.

Therefore, the student's solution has a flaw: it does not handle the case when the missing number is the first element (i.e., the array does not start with 1).

But the problem says "range of 1 to n", meaning the numbers are between 1 and n. So if the array does not contain 1, then 1 is missing. Similarly, if the array does not contain n, then n is missing.

The student's code returns -1 only in two cases: empty list and when the condition (list[0] + list.size - 1 == list[list.size - 1]) is true. But we saw that condition is incorrect because it returns -1 when the missing number is at the end (like [1,2,3,4] for n=5) when it should return 5.

So the student's solution has multiple issues.

Now, let's compare with the reference solution.

The reference solution in C++:

int search(int ar[], int size) {
int a = 0, b = size - 1;
int mid;

while ((b - a) > 1) {
    mid = (a + b) / 2;
    if ((ar[a] - a) != (ar[mid] - mid))
        b = mid;
    else if ((ar[b] - b) != (ar[mid] - mid))
        a = mid;
}

return ar[a] + 1;

}


The reference solution uses a different approach. It checks the difference between the value and the index. For a contiguous array, ar[i] - i should be constant (equal to the first value). So when there is a missing number, the difference will change.

The reference solution maintains two pointers a and b. It checks the left half and right half. It returns ar[a] + 1.

Let's test the reference solution with the same examples.

Example: [2,3,4] (missing 1). 
size=3.
a=0, b=2.
while (2-0>1) -> true.
mid=1.
Check: ar[a]-a = 2-0=2, ar[mid]-mid=3-1=2 -> equal.
Then check: ar[b]-b=4-2=2, ar[mid]-mid=2 -> equal? So both conditions are equal? Then it doesn't update? Actually, the code has two conditions: if the left part has inconsistency, then set b to mid. Else if the right part has inconsistency, set a to mid. But in this case, both are consistent? So the while loop would continue? But wait, the condition is only if the left part has inconsistency OR the right part has inconsistency. But here both are consistent? So the loop would break? Actually no, because the while condition is (b-a>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants